﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Linq.Expressions;
using Microsoft.Scripting.Utils;
using System.Reflection;
using System.Reflection.Emit;

namespace Scala.Compiler.Ast
{
    public class ScalaAst : Tree
    {
        private Statement _body;
        internal int[] _lineLocations;
        private readonly string _name;
        private readonly ModuleBuilder _modBldr;
        private readonly AssemblyBuilder _assmBldr;
        private readonly AssemblyName _assmName;

        public ScalaAst(string name, Statement body)
        {
            ContractUtils.RequiresNotNull(body, "body");
            _body = body;
            _name = name;

            AppDomain scalaDomain = AppDomain.CurrentDomain;
            _assmName = new AssemblyName(name);
            _assmBldr = scalaDomain.DefineDynamicAssembly(_assmName, AssemblyBuilderAccess.RunAndSave);
            _modBldr = _assmBldr.DefineDynamicModule(_assmName.Name, _assmName.Name + ".dll");
        }

        public ScalaAst(string name)
        {
            _name = name;
            AppDomain scalaDomain = AppDomain.CurrentDomain;
            _assmName = new AssemblyName(name);
            _assmBldr = scalaDomain.DefineDynamicAssembly(_assmName, AssemblyBuilderAccess.RunAndSave);
            _modBldr = _assmBldr.DefineDynamicModule(_assmName.Name, _assmName.Name + ".dll");
        }

        public void ParsingFinished(int[] lineLocations, Statement body)
        {
            ContractUtils.RequiresNotNull(body, "body");
            if (_body != null)
            {
                throw new InvalidOperationException("cannot set body twice");
            }
            _body = body;
        }

        public string Name
        {
            get { return _name; }
        }

        public ModuleBuilder ModuleBuilder { get { return _modBldr; } }
        public AssemblyBuilder AssemblyBuilder { get { return _assmBldr; } }

        public Statement Body { get { return _body; } set { _body = value; } }

        public override void Walk(ScalaWalker walker)
        {
            if (walker.Walk(this))
            {
                if (_body != null)
                {
                    _body.Walk(walker);
                }
            }
            walker.PostWalk(this);
        }

        public override Microsoft.Scripting.Ast.Expression Reduce()
        {
            Body.Reduce();
            return Expression.Empty();
        }

        public void CreateTypes()
        {
            ScalaBinder.BindAst(this);
            ScalaTypeNamer.TypeNameAst(this);
            ScalaTyper.TypeAst(this);
            
            Reduce();
            AssemblyBuilder.Save(_assmName.Name+ ".dll");
        }
    }
}
